﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using EnvDTE80;
using System.IO;
using System.Xml;
using System.Reflection;

namespace BaseAddIn.Analysis
{
    public sealed class FXCop : Base
    {
        #region Campos

        private string _caminhoFXCop;
        private string _caminhoRules;
        private ProjectInfo.RuleFilesList _rules;

        #endregion

        #region Construtor

        public FXCop(string caminhoRules, string caminhoFXCop, ProjectInfo.RuleFilesList projetoRules)
            : base()
        {
            _caminhoFXCop = caminhoFXCop;
            _rules = projetoRules;
            _caminhoRules = caminhoRules;
        }

        #endregion

        #region Public Methods

        public sealed override void Analise(Project projetoAnalisar)
        {
            string xmlPath = GenerateXmlFxCop(projetoAnalisar);

            if (File.Exists(xmlPath))
            {
                CapturaErros(xmlPath);
                File.Delete(xmlPath);
            }
            else
            {
               GeraErroXmlNaoEncontrado(Path.GetDirectoryName(xmlPath));
            }
        }

        #endregion

        #region Private Methods

        private void GeraErroXmlNaoEncontrado(string diretorio)
        {
            string erro = String.Concat("Não foi possível encontrar a análise do FXCop no caminho: ",
                                        diretorio);
            ListaProblemas.Add(new Rules.ProblemAddIn(erro,
                                                      Microsoft.VisualStudio.Shell.TaskPriority.High,
                                                      diretorio,
                                                      0,
                                                      0));
        }

        private string GenerateXmlFxCop(Project projeto)
        {
            System.Diagnostics.Process procStart = null;
            string binDir = null;
            string xmlPath = null;
            string outputPath = null;
            string outputFileName = null;
            DirectoryInfo fullPath = null;
            int index;
            string fxCopProject = null;

            fullPath = new DirectoryInfo(projeto.Properties.Item("FullPath").Value.ToString());
            outputPath = projeto.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString();
            outputFileName = projeto.Properties.Item("OutputFileName").Value.ToString();

            index = outputPath.IndexOf("..\\");

            while (index >= 0)
            {
                fullPath = fullPath.Parent;
                outputPath = outputPath.Remove(index, 3);

                index = outputPath.IndexOf("..\\");
            }

            binDir = (Path.Combine(fullPath.FullName, outputPath));
            xmlPath = Path.Combine(binDir, String.Concat("FxCopAnalysis_",
                                                         Path.GetFileNameWithoutExtension(projeto.FileName),
                                                         ".xml"));

            fxCopProject = Path.Combine(binDir, "TempFXCopProject.FxCop");

            if (File.Exists(xmlPath))
                File.Delete(xmlPath);

            if (File.Exists(fxCopProject))
                File.Delete(fxCopProject);

            PreparaFXCopProject(fxCopProject, Path.Combine(binDir,outputFileName));

            procStart = new System.Diagnostics.Process();
            procStart.StartInfo.FileName = _caminhoFXCop;
            procStart.StartInfo.Arguments = String.Concat("/c /s /forceoutput ",
                                                          "/project:\"", fxCopProject, "\" ",
                                                          "/out:\"", xmlPath, "\" ");

            procStart.StartInfo.UseShellExecute = false;
            procStart.StartInfo.CreateNoWindow = true;
            procStart.StartInfo.RedirectStandardInput = false;

            //Instancia do servidor Named Pipe
            NamedPipe.Server.ServerConnect serverConnect = new NamedPipe.Server.ServerConnect(_caminhoRules);

            procStart.Start();

            //Ativando servidor Named Pipe
            serverConnect.ActiveServer();

            procStart.WaitForExit();

            //Parando servidor Named Pipe
            serverConnect.Stop();

            if (File.Exists(fxCopProject))
                File.Delete(fxCopProject);

            return xmlPath;
        }

        private void PreparaFXCopProject(string nomeProjeto, string outputFileName)
        {
            Stream resourceStream;
            XmlReaderSettings xmlRdrSettings;
            XmlReader xmlRdr;
            XmlNode nodeTargets;
            XmlNode nodeRuleFiles;
            XmlNode nodeAdiconar;
            XmlNode nodeAdiconar2;
            XmlAttribute atributoAdicionar;
            XmlAttribute atributoAdicionar2;
            XmlDocument _doc;
            string tipoFXCopFullName = typeof(Rules.BaseRuleFXCop).FullName;

            resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("BaseAddIn.ProjectInfo.ProjetoVazioFXCop.FxCop");
            xmlRdrSettings = new XmlReaderSettings();
            xmlRdr = XmlReader.Create(resourceStream, xmlRdrSettings);
            _doc = new XmlDocument();

            _doc.Load(xmlRdr);

            xmlRdr.Close();
            resourceStream.Close();

            nodeTargets = _doc["FxCopProject"]["Targets"];
            nodeRuleFiles = _doc["FxCopProject"]["Rules"]["RuleFiles"];

            #region Criação de Target

            nodeAdiconar = _doc.CreateElement("Target");

            atributoAdicionar = _doc.CreateAttribute("Analyze");
            atributoAdicionar.Value = "True";
            nodeAdiconar.Attributes.Append(atributoAdicionar);

            atributoAdicionar = _doc.CreateAttribute("AnalyzeAllChildren");
            atributoAdicionar.Value = "True";
            nodeAdiconar.Attributes.Append(atributoAdicionar);

            atributoAdicionar = _doc.CreateAttribute("Name");
            atributoAdicionar.Value = outputFileName;
            nodeAdiconar.Attributes.Append(atributoAdicionar);

            nodeTargets.AppendChild(nodeAdiconar);

            #endregion

            #region Criação de Rules

            foreach (ProjectInfo.RuleFile ruleFile in _rules)
            {
                nodeAdiconar = _doc.CreateElement("RuleFile");

                atributoAdicionar = _doc.CreateAttribute("Enabled");
                atributoAdicionar.Value = ruleFile.Enabled.ToString();
                nodeAdiconar.Attributes.Append(atributoAdicionar);

                atributoAdicionar = _doc.CreateAttribute("AllRulesEnabled");
                atributoAdicionar.Value = ruleFile.AllRulesEnabled.ToString();
                nodeAdiconar.Attributes.Append(atributoAdicionar);

                atributoAdicionar = _doc.CreateAttribute("Name");
                atributoAdicionar.Value = ruleFile.Name;
                nodeAdiconar.Attributes.Append(atributoAdicionar);

                foreach (ProjectInfo.Rule rule in ruleFile.Rules)
                {
                    if (rule.TipoRegra == tipoFXCopFullName)
                    {
                        nodeAdiconar2 = _doc.CreateElement("Rule");

                        atributoAdicionar2 = _doc.CreateAttribute("Enabled");
                        atributoAdicionar2.Value = rule.Enabled.ToString();
                        nodeAdiconar2.Attributes.Append(atributoAdicionar2);

                        atributoAdicionar2 = _doc.CreateAttribute("Name");
                        atributoAdicionar2.Value = rule.Name;
                        nodeAdiconar2.Attributes.Append(atributoAdicionar2);

                        nodeAdiconar.AppendChild(nodeAdiconar2);
                    }
                }

                nodeRuleFiles.AppendChild(nodeAdiconar);
            }

            #endregion

            _doc.Save(nomeProjeto);
        }

        private void CapturaErros(string xmlPath)
        {
            MemoryStream mmoStream;
            XmlReaderSettings xmlRdrSettings;
            XmlReader xmlRdr;
            XmlNode namespacesNodePai;
            XmlNode targetsNodePai;
            XmlDocument _doc;

            mmoStream = new MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(File.ReadAllText(xmlPath, System.Text.UTF8Encoding.UTF8)));
            xmlRdrSettings = new XmlReaderSettings();
            xmlRdr = XmlReader.Create(mmoStream, xmlRdrSettings);
            _doc = new XmlDocument();

            _doc.Load(xmlRdr);

            xmlRdr.Close();
            mmoStream.Close();

            namespacesNodePai = _doc["FxCopReport"]["Namespaces"];
            targetsNodePai = _doc["FxCopReport"]["Targets"];

            if (namespacesNodePai != null)
                AnalisaNodeErro(RetornaListaErros(namespacesNodePai));

            if (targetsNodePai != null)
                AnalisaNodeErro(RetornaListaErros(targetsNodePai));
        }

        private void AnalisaNodeErro(List<XmlNode> issueNodes)
        {
            Rules.ProblemAddIn problema;
            string erro;
            Microsoft.VisualStudio.Shell.TaskPriority priority;
            string documento;
            int line;

            foreach (XmlNode node in issueNodes)
            {
                erro = node.InnerText;

                if (node.Attributes["Level"] == null)
                {
                    priority = Microsoft.VisualStudio.Shell.TaskPriority.Normal;
                }
                else
                {
                    if (node.Attributes["Level"].Value.Contains("Critical"))
                        priority = Microsoft.VisualStudio.Shell.TaskPriority.High;
                    else
                        priority = Microsoft.VisualStudio.Shell.TaskPriority.Normal;
                }

                if (node.Attributes["Line"] != null)
                {
                    line = Convert.ToInt32(node.Attributes["Line"].Value) - 1;
                }
                else
                {
                    line = 1;
                }

                if (node.Attributes["Path"] != null
                    && node.Attributes["File"] != null)
                    documento = Path.Combine(node.Attributes["Path"].Value, node.Attributes["File"].Value);
                else
                    documento = String.Empty;

                problema = new Rules.ProblemAddIn(erro,
                                                  priority,
                                                  documento,
                                                  line,
                                                  0);

                ListaProblemas.Add(problema);
            }
        }

        private List<XmlNode> RetornaListaErros(XmlNode nodePai)
        {
            List<XmlNode> retorno = new List<XmlNode>();

            foreach (XmlNode node in nodePai)
            {
                if (node.Name == "Issue")
                    retorno.Add(node);
                else
                    retorno.AddRange(RetornaListaErros(node));
            }

            return retorno;
        }


        #endregion

        #region IDisposable Members

        public new void Dispose()
        {
            base.Dispose();
        }

        #endregion
    }
}
